package puzzle.common.imgreader;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.util.Arrays;

import tomoto.util.IntVectorUtil;

public class ImgMatcher {

	public static void match(BufferedImage img, ImgPattern[] patterns, Dimension gridSize,
			Dimension gridCount, int[] result) {
		int width = img.getWidth();
		int height = img.getHeight();
		int[] pixels = createIntegerPixels(img, width, height);

		for (int gy = 0; gy < gridCount.height; gy++) {
			for (int gx = 0; gx < gridCount.width; gx++) {
				processGrid(pixels, width, height, patterns, gridSize, gridCount, result, gy, gx);
			}
		}
	}

	private static void processGrid(int[] pixels, int width, int height, ImgPattern[] patterns,
			Dimension gridSize, Dimension gridCount, int[] result, int gy, int gx) {
		int x0 = gx * gridSize.width;
		int y0 = gy * gridSize.height;
		final int inset = 2;

		int[] distances = new int[patterns.length];
		Arrays.fill(distances, Integer.MAX_VALUE);

		for (int y = y0 + inset; y < y0 + gridSize.height - inset; y++) {
			for (int x = x0 + inset; x < x0 + gridSize.width - inset; x++) {
				for (int i = 0; i < patterns.length; i++) {
					ImgPattern p = patterns[i];
					int ox = p.width / 2;
					int oy = p.height / 2;
					int d = manhattanDistance(pixels, x - ox, y - oy, width, height, p);
					if (d < distances[i] && d < p.threshold) {
						distances[i] = d;
					}
				}
			}
		}

		int minIndex = -1;
		int minDistance = Integer.MAX_VALUE;
		for (int i = 0; i < distances.length; i++) {
			if (minDistance > distances[i]) {
				if (minIndex < 0 || patterns[minIndex].priority >= patterns[i].priority) {
					minIndex = i;
					minDistance = distances[i];
				}
			}
		}

		result[gx + gy * gridCount.width] = minIndex;
	}

	public static BufferedImage createDistanceImage(BufferedImage img, ImgPattern pattern) {
		int w = img.getWidth();
		int h = img.getHeight();
		BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		int[] pixels = createIntegerPixels(img, w, h);
		int ox = pattern.width / 2;
		int oy = pattern.height / 2;
		int patternAreaSize = pattern.width * pattern.height;

		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				int d = manhattanDistance(pixels, x - ox, y - oy, w, h, pattern);
				int d1 = d / patternAreaSize;
				assert d1 >= 0 && d1 <= 0xff;
				int b = 0xff - d1;
				int rgb = (d < pattern.threshold) ? (b + (b << 8) + (b << 16)) : (b << 16);
				result.setRGB(x, y, rgb);
			}
		}

		return result;
	}

	private static int manhattanDistance(int[] pixels, int x0, int y0, int width, int height,
			ImgPattern pattern) {
		int x1 = x0 + pattern.width;
		int y1 = y0 + pattern.height;

		int distance = 0;
		int patternPixelIndex = 0;

		for (int y = y0; y < y1; y++) {
			int pixelIndex = y * width + x0;
			for (int x = x0; x < x1; x++) {
				int p;
				if (x >= 0 && x < width && y >= 0 && y < height) {
					p = pixels[pixelIndex];
				} else {
					p = 0;
				}
				distance += Math.abs(pattern.pixels[patternPixelIndex] - p);
				pixelIndex++;
				patternPixelIndex++;
			}
		}
		return distance;
	}

	private static int[] createIntegerPixels(BufferedImage img, int width, int height) {
		int[] pixels = img.getRGB(0, 0, width, height, null, 0, width);
		IntVectorUtil.logand(pixels, 0xff); // the image must be gray scale
		return pixels;
	}
}
